﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;



/// <summary>
/// 可枚举扩展
/// </summary>
public static class EnumerableExtensions
{
    /// <summary>
    /// 通用可枚举
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class GenericEnumerable<T> : IEnumerable<T>
    {
        private readonly IEnumerable source;

        /// <summary>
        /// 初始化一个可枚举类
        /// </summary>
        /// <param name="source">The source.</param>
        public GenericEnumerable(IEnumerable source)
        {
            this.source = source;
        }
        /// <summary>
        /// 获得一个枚举器
        /// </summary>
        /// <returns>可用于循环访问集合的 System.Collections.IEnumerator 对象</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.source.GetEnumerator();
        }
        /// <summary>
        /// 获得一个枚举器（泛型类）
        /// </summary>
        /// <returns>可用于循环访问指定类型集合的 System.Collections.IEnumerator 对象</returns>
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            foreach (T item in this.source)
            {
                yield return item;
            }
        }
    }

    /// <summary>
    /// 循环可枚举对象并且对每一个元素执行传入的代理
    /// </summary>
    /// <param name="source">要操作的可枚举的对象</param>
    /// <param name="action">要执行的代理</param>
    /// <typeparam name="T">可枚举的泛型类型</typeparam>
    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
        }
    }

    /// <summary>
    /// 循环可枚举的项并为每个项执行传入的action操作
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="action"></param>
    /// <typeparam name="T"></typeparam>
    public static void Each<T>(this IEnumerable<T> instance, Action<T, int> action)
    {
        int index = 0;
        foreach (T item in instance)
            action(item, index++);
    }

    /// <summary>
    /// 执行每项中提供的委托函数
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    /// <param name="instance">指定类型的枚举实例</param>
    /// <param name="action">运用于每项的委托函数</param>
    public static void Each<T>(this IEnumerable<T> instance, Action<T> action)
    {
        foreach (T item in instance)
        {
            action(item);
        }
    }
    /// <summary>
    /// 把一个枚举接口转化成泛型枚举
    /// </summary>
    /// <param name="source">普通枚举数据源</param>
    /// <returns>泛型枚举</returns>
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
    internal static IEnumerable AsGenericEnumerable(this IEnumerable source)
    {
        Type elementType = typeof(Object);

        Type type = source.GetType().FindGenericType(typeof(IEnumerable<>));
        if (type != null)
        {
            return source;
        }

        IEnumerator enumerator = source.GetEnumerator();

        while (enumerator.MoveNext())
        {
            if (enumerator.Current != null)
            {
                elementType = enumerator.Current.GetType();
                try
                {
                    enumerator.Reset();
                }
                catch
                {
                }
                break;
            }
        }

        Type genericType = typeof(GenericEnumerable<>).MakeGenericType(elementType);
        var constructorParameters = new object[] { source };

        return (IEnumerable)Activator.CreateInstance(genericType, constructorParameters);
    }
    /// <summary>
    /// 索引位置
    /// </summary>
    /// <param name="source">枚举实例</param>
    /// <param name="item">指定项</param>
    /// <returns>指定项在枚举实例中第一次出现的位置</returns>
    internal static int IndexOf(this IEnumerable source, object item)
    {
        int index = 0;
        foreach (object i in source)
        {
            if (Equals(i, item))
            {
                return index;
            }

            index++;
        }

        return -1;
    }

    /// <exception cref="ArgumentOutOfRangeException"><c>索引</c> 超出数范围.</exception>
    internal static object ElementAt(this IEnumerable source, int index)
    {
        if (index < 0)
        {
            throw new ArgumentOutOfRangeException("index");
        }

        var list = source as IList;
        if (list != null && list.Count > 0)
        {
            return list[index];
        }

        foreach (var item in source)
        {
            if (index == 0)
            {
                return item;
            }

            index--;
        }

        return null;
    }

    /// <summary>
    /// 递归查询枚举
    /// </summary>
    /// <param name="source">数据源</param>
    /// <param name="recursiveSelector">递归查询委托</param>
    /// <typeparam name="TSource">枚举泛型类型</typeparam>
    /// <returns>根据递归查询委托查询的结果</returns>
    public static IEnumerable<TSource> SelectRecursive<TSource>(this IEnumerable<TSource> source,
        Func<TSource, IEnumerable<TSource>> recursiveSelector)
    {
        var stack = new Stack<IEnumerator<TSource>>();
        stack.Push(source.GetEnumerator());

        try
        {
            while (stack.Count > 0)
            {
                if (stack.Peek().MoveNext())
                {
                    TSource current = stack.Peek().Current;

                    yield return current;

                    IEnumerable<TSource> children = recursiveSelector(current);
                    if (children != null)
                    {
                        stack.Push(children.GetEnumerator());
                    }
                }
                else
                {
                    stack.Pop().Dispose();
                }
            }
        }
        finally
        {
            while (stack.Count > 0)
            {
                stack.Pop().Dispose();
            }
        }
    }

    /// <exception cref="ArgumentNullException"><c>类型一</c>为空.</exception>
    /// <exception cref="ArgumentNullException"><c>类型二</c>为空.</exception>
    /// <exception cref="ArgumentNullException"><c>resultSelector</c> 为空.</exception>
    /// <summary>
    /// 迭代压缩
    /// </summary>
    /// <typeparam name="TFirst">参数类型一</typeparam>
    /// <typeparam name="TSecond">参数类型二</typeparam>
    /// <typeparam name="TResult">返回参数类型</typeparam>
    /// <param name="first">需要扩展的枚举实例</param>
    /// <param name="second">枚举实例</param>
    /// <param name="resultSelector">结果查询委托</param>
    /// <returns>迭代压缩后的泛型枚举</returns>
    public static IEnumerable<TResult> Consolidate<TFirst, TSecond, TResult>(
        this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");
        if (resultSelector == null) throw new ArgumentNullException("resultSelector");

        return ZipIterator(first, second, resultSelector);
    }

    private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(
        IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
        using (IEnumerator<TFirst> e1 = first.GetEnumerator())
        using (IEnumerator<TSecond> e2 = second.GetEnumerator())
            while (e1.MoveNext() && e2.MoveNext())
                yield return resultSelector(e1.Current, e2.Current);
    }

    /// <summary>
    /// 转化一个枚举为只读集合
    /// </summary>
    /// <param name="sequence">源泛型枚举</param>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <returns>返回转化后只读集合</returns>
    public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(this IEnumerable<T> sequence)
    {
        if (sequence == null)
        {
            return DefaultReadOnlyCollection<T>.Empty;
        }
        var onlys = sequence as ReadOnlyCollection<T>;
        if (onlys != null)
        {
            return onlys;
        }
        return new ReadOnlyCollection<T>(sequence.ToArray());
    }

    private static class DefaultReadOnlyCollection<T>
    {
        private static ReadOnlyCollection<T> defaultCollection;

        internal static ReadOnlyCollection<T> Empty
        {
            get { return defaultCollection ?? (defaultCollection = new ReadOnlyCollection<T>(new T[0])); }
        }
    }
}
